﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace dotNetInteropPlugin.CSharpMixingLibrary
{
    internal delegate void PluginLoadUnloadEvent(PluginInformation sender);

    /// <summary>
    /// Информация о плагине
    /// </summary>
    internal class PluginInformation: IDisposable
    {
        private static int CurUniquePluginID = 100;
        private static int GetNewUniqPluginID()
        {   
            return System.Threading.Interlocked.Increment(ref CurUniquePluginID);
        }

        public PluginInformation(FileInfo asmblPath, string asmblName, string clsName, PluginDev.AIMPManagedPluginAttribute plgInf)
        {
            inPathToAssembly = asmblPath;
            PluginClassName = clsName;
            PluginAssemblyName = asmblName;
            PluginInfo = plgInf;
            LoadedPlugin = null;
        }
        public PluginInformation(string asmblPath, string asmblName, string clsName, PluginDev.AIMPManagedPluginAttribute plgInf)
        {
            inPathToAssembly = new FileInfo(asmblPath);
            PluginClassName = clsName;
            PluginAssemblyName = asmblName;
            PluginInfo = plgInf;
            LoadedPlugin = null;
        }

        private FileInfo inPathToAssembly;
        /// <summary>
        /// Путь до сборки
        /// </summary>
        public string AssemblyFileName
        {
            get
            {
                return inPathToAssembly.Name;
            }
        }
        /// <summary>
        /// Имя сборки плагина
        /// </summary>
        public string PluginAssemblyName
        {
            get;
            private set;
        }
        /// <summary>
        /// Имя класса плагина
        /// </summary>
        public string PluginClassName
        {
            get;
            private set;
        }
        /// <summary>
        /// Имеет ли диалог настроек
        /// </summary>
        public bool HasSettingDialog
        {
            get
            {
                if (IsLoaded)
                    return LoadedPlugin.HasSettingDialog;
                return false;
            }
        }
        /// <summary>
        /// Загружен ли плагин
        /// </summary>
        public bool IsLoaded
        {
            get
            {
                return LoadedPlugin != null;
            }
        }
        /// <summary>
        /// Подробная информация о плагине
        /// </summary>
        public PluginDev.AIMPManagedPluginAttribute PluginInfo
        {
            get;
            private set;
        }
        /// <summary>
        /// Собственно объект плагина
        /// </summary>
        public PluginDev.MAIMPManagedPluginFirstLevel LoadedPlugin
        {
            get;
            private set;
        }
        /// <summary>
        /// Домен плагина (null = текущий домен)
        /// </summary>
        public System.AppDomain PluginAppDomainInfo
        {
            get;
            private set;
        }

        public event PluginLoadUnloadEvent PluginLoadEvent;
        public event PluginLoadUnloadEvent PluginUnloadEvent;


        public bool Load()
        {
            if (!IsLoaded)
            {
                if (PluginInfo.RequireAppDomain)
                { // Нужен отдельный домен
                    try
                    {
                        AppDomainSetup dmnSetup = new AppDomainSetup();

                        dmnSetup.ApplicationName = PluginInfo.Name;
                        dmnSetup.ApplicationBase = inPathToAssembly.DirectoryName;
                        PluginAppDomainInfo = AppDomain.CreateDomain(PluginInfo.Name + "_domain" + Guid.NewGuid().ToString().GetHashCode().ToString("x"), null, dmnSetup);

                        LoadedPlugin = (PluginDev.MAIMPManagedPluginFirstLevel)PluginAppDomainInfo.CreateInstanceFromAndUnwrap(inPathToAssembly.FullName, PluginClassName);

               //         object Tst = PluginAppDomainInfo.CreateInstanceFromAndUnwrap(System.Reflection.Assembly.GetExecutingAssembly().Location, "dotNetInteropPlugin.PluginDev.MAIMP2ControllerInitializer");

                    }
                    catch (Exception ex)
                    {
                        if (PluginAppDomainInfo != null)
                        {
                            AppDomain.Unload(PluginAppDomainInfo);
                            PluginAppDomainInfo = null;
                        }
                        LoadedPlugin = null;
#if DEBUG
                        System.Windows.Forms.MessageBox.Show(ex.Message);
#endif
                    }
                }
                else
                {  // домен не нужен
                    System.Reflection.Assembly asm = null;
                    try
                    { // проверяем загружена ли уже сборка
                        asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(o => o.FullName == PluginAssemblyName);
                    }
                    catch (Exception ex)
                    {
#if DEBUG
                        System.Windows.Forms.MessageBox.Show(ex.Message);
#endif
                    }

                    if (asm == null)
                    {
                        try
                        {  // грузим сборку? если она не была загружена
                            asm = System.Reflection.Assembly.LoadFrom(inPathToAssembly.FullName);
                        }
                        catch (Exception ex)
                        {
#if DEBUG
                            System.Windows.Forms.MessageBox.Show(ex.Message);
#endif
                        }
                    }

                    if (asm != null)
                    {
                        try
                        {  // создаём объект
                            Type instType = asm.GetType(PluginClassName);
                            LoadedPlugin = (PluginDev.MAIMPManagedPluginFirstLevel)Activator.CreateInstance(instType);
                        }
                        catch (Exception ex)
                        {
#if DEBUG
                            System.Windows.Forms.MessageBox.Show(ex.Message);
#endif
                        }
                    }
                }
                if (IsLoaded)
                    this.LoadedPlugin._UniqueID = GetNewUniqPluginID();
                if (IsLoaded && PluginLoadEvent != null)
                {
                    try
                    {
                        PluginLoadEvent(this);
                    }
                    catch
                    {
                        Unload();
                    }
                }
            }
            return IsLoaded;
        }



        public bool Unload()
        {
            if (IsLoaded)
            {
                try
                {
                    if (PluginUnloadEvent != null)
                        PluginUnloadEvent(this);

                    LoadedPlugin.OnDispose();

                    if (PluginAppDomainInfo != null)
                    {
                        AppDomain.Unload(PluginAppDomainInfo);
                    }

                    PluginAppDomainInfo = null;
                    LoadedPlugin = null;

                    GC.Collect();
                }
                catch (Exception ex)
                {
#if DEBUG
                    System.Windows.Forms.MessageBox.Show(ex.Message);
#endif
                }
            }
            return !IsLoaded;
        }
        public void ShowSettingDialog(System.Windows.Forms.IWin32Window ParentWnd)
        {
            if (IsLoaded)
                LoadedPlugin.ShowSettingDialog(ParentWnd);
        }
        public void Initialize(dotNetInteropPlugin.PluginDev.IMAIMPPlayerFirstLevel plr)
        {
            if (IsLoaded)
                LoadedPlugin.OnInitialize(plr, LoadedPlugin._UniqueID);
        }


        public void Dispose()
        {
            if (IsLoaded)
                Unload();
        }
    }
}
